#include "Map.h"
#include <fstream>
#include <iostream>
#include <string>
#include <omp.h>

#define  TREESIZE 4
#define CHANCE_THAT_TREE_IGNITES 10
#define CHANCE_THAT_TREE_GROWS 1
using namespace std;

Map::Map(void)
{
	width = height = 0;
	current_map = next_map = 0;
}


Map::~Map(void)
{
}

void Map::Initialize(int h, int w)
{
	width = w;
	height = h;
	current_map = new int[width * height];
	next_map = new int[width * height];
	drawCoordinates = CL_Pointf(0,0);
	omp_set_num_threads(4);
	for(int i = 0; i < width * height; i++)
	{
		current_map[i] = TREE;
	}

	current_map[4500] = FIRE;
	current_map[12] = FIRE;
	current_map[8947] = FIRE;
	current_map[1245] = FIRE;
}

void Map::ReadInputFile(string inputFileName)
{
	int x = -1, y = 0, c = 0;
	string line;
	ifstream myfile (inputFileName);

	while(getline(myfile, line)) {
		if(x!=-1)
		{
			for(y=0; y < line.size(); y++)
			{
				current_map[c] = line[y];
				next_map[c] = line[y];
				c++;
			}
		}
		x++;
	}

	myfile.close();
}

void Map::NextGeneration()
{
	int x, y, neighbor_count;

#pragma omp for nowait
	for(y = 1; y < height-1; y++)
	{
		
		CheckCell(0,y,LEFT);
		CheckCell(width-1,y,RIGHT);

		for(x = 1; x < width-1; x++)
		{
			CheckCell(x,y,MIDDLE);
		}
	}
	
//top + bottom
#pragma omp for nowait
	for(x = 1; x < width-1; x++)
	{
		CheckCell(x,0,TOP);
		CheckCell(x,height-1,BOTTOM);

	}

	//corners
#pragma omp single nowait 
	{
		CheckCell(0,0, TL);

		CheckCell(0, height-1, BL);

		CheckCell(width-1, height-1, BR);

		CheckCell(width-1, 0, TR);
	}
}

void Map::CopyCells()
{
	int* tmp = current_map;
	current_map = next_map;
	next_map = tmp;
}

int Map::CellState(int x, int y)
{
	int tmp = y * width + x;
	return current_map[tmp];
}

void Map::CheckCell(int x, int y, int type)
{
	int neighbor_count = 0;

	//zelle mit brennenden baum wird leer
	if(CellState(x,y) == FIRE)
	{
		SetCell(x,y,EMPTY);
	}
	//zelle mit baum
	if(CellState(x,y) == TREE)
	{
		switch(type)
		{
		case LEFT:
			neighbor_count = GetNeighbor(0, y-1) + GetNeighbor(1, y-1) + GetNeighbor(1, y) + GetNeighbor(1, y+1) + GetNeighbor(0, y+1);	
			break;
		case RIGHT:
			neighbor_count = GetNeighbor(width-1, y-1) + GetNeighbor(width-2, y-1) + GetNeighbor(width-2, y) + GetNeighbor(width-2, y+1) + GetNeighbor(width-1, y+1);	
			break;
		case MIDDLE:
			neighbor_count = GetNeighbor(x-1, y-1) + GetNeighbor(x, y-1) 
				+ GetNeighbor(x+1, y-1) + GetNeighbor(x-1, y) 
				+ GetNeighbor(x+1, y) + GetNeighbor(x-1, y+1) 
				+ GetNeighbor(x, y+1) + GetNeighbor(x+1, y+1);
			break;
		case TOP:
			neighbor_count = GetNeighbor(x-1, 0) + GetNeighbor(x-1, 1) + GetNeighbor(x, 1) + GetNeighbor(x+1, 1) + GetNeighbor(x+1, 0);
			break;
		case BOTTOM:
			neighbor_count = GetNeighbor(x-1, height-1) + GetNeighbor(x-1, height-2) + GetNeighbor(x, height-2) + GetNeighbor(x+1, height-2) + GetNeighbor(x+1, height-1);
			break;
		case TL:
			neighbor_count = GetNeighbor(1, 0) + GetNeighbor(1,1) + GetNeighbor(0,1);
			break;
		case TR:
			neighbor_count = GetNeighbor(width-2, 0), + GetNeighbor(width-2, 1) + GetNeighbor(width-1, 1);
			break;
		case BL:
			neighbor_count = GetNeighbor(0, height-2) + GetNeighbor(1, height-2) + GetNeighbor(1, height-1);
			break;
		case BR:
			neighbor_count = GetNeighbor(width-2, height-1) + GetNeighbor(width-2, height-2) + GetNeighbor(width-1, height-2);
			break;
		}
		//wird zu feuer, wenn mindestens 1 nachbar brennt
		
		if(neighbor_count != 0)
		{
			SetCell(x,y, FIRE);
		}
		//ohne brennende nachbarn...
		else
		{
			if(CalculateNextState(CHANCE_THAT_TREE_IGNITES))
			{
				SetCell(x,y,FIRE);
			}
			else
			{
				SetCell(x,y,TREE);
			}
		}
	}
	//eine leere zelle wird zu einem baum mit der wahrscheinlichkeit g,...
	if(CellState(x,y) == EMPTY)
	{
		if(CalculateNextState(CHANCE_THAT_TREE_GROWS))
		{
			SetCell(x,y,TREE);
		}
		else
		{
			SetCell(x,y,EMPTY);
		}
	}
}

int Map::GetNeighbor(int x, int y)
{
	int tmp = y * width + x;

	if(current_map[tmp] == FIRE)
		return 1;
	else
		return 0;
}
void Map::Draw(CL_GraphicContext gc)
{
	CL_Colorf red(255/255.0f, 0/255.0f, 0/255.0f);
	CL_Colorf green(0/255.0f, 255/255.0f, 0/255.0f);
	CL_Colorf black(50/255.0f, 0/255.0f, 0/255.0f);
	for(int i = 0; i < height * width; i++)
	{
		if(i % width == 0 && i!=0)
		{
			drawCoordinates.y += TREESIZE;
			drawCoordinates.x = 0;
		}
		if(current_map[i] == FIRE)
		{
			CL_Draw::fill(gc,drawCoordinates.x,drawCoordinates.y,drawCoordinates.x + TREESIZE, drawCoordinates.y + TREESIZE, red);
		}
		else if(current_map[i] == TREE)
		{
			CL_Draw::fill(gc,drawCoordinates.x,drawCoordinates.y,drawCoordinates.x + TREESIZE, drawCoordinates.y + TREESIZE, green);
		}
		else
		{
			CL_Draw::fill(gc,drawCoordinates.x,drawCoordinates.y,drawCoordinates.x + TREESIZE, drawCoordinates.y + TREESIZE, black);
		}
		drawCoordinates.x += TREESIZE;
	}
	drawCoordinates.x = drawCoordinates.y = 0;
}

void Map::SetCell(int x, int y, int nextState)
{
	next_map[y*width + x] = nextState;
}

bool Map::CalculateNextState(int chance)
{
	random_device seed;  
	mt19937 gen(seed());
	uniform_int<> dist(1, 100);  

	std::function<int()> rnd = std::bind(dist, gen); 
	int nr = rnd();
	
	return nr <= chance;

}